﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace ParseLogFile
{
    class Program
    {
        enum LogLevel
        {
            Trace = 1,
            Debug = 2,
            Info = 3,
            Warn = 4,
            Error = 5,
            Fatal = 6
        }
        class LogRecord
        {
            public long RecordId { get; set; }
            public DateTime Time { get; set; }
            public string Logger { get; set; }
            public LogLevel Level { get; set; }
            public int ThreadId { get; set; }
            public string Message { get; set; }
            public string Exception { get; set; }
            public bool HasException
            {
                get { return !string.IsNullOrEmpty(Exception); }
            }
            public override string ToString()
            {
                return $"{Time}|{Logger}|{Level}|{ThreadId}|{Message}";
            }
            public static LogRecord ReadFromRegexMatch(Match regMatch)
            {
                LogRecord log = null;
                if (regMatch.Success)
                {
                    log = new LogRecord();
                    var timeReg = regMatch.Groups["time"];
                    if (timeReg.Success)
                    {
                        if (DateTime.TryParse(timeReg.Value, out DateTime time))
                        {
                            log.Time = time;
                        }
                    }
                    var loggerReg = regMatch.Groups["logger"];
                    if (loggerReg.Success)
                    {
                        var loggerStr = loggerReg.Value;
                        if (!string.IsNullOrEmpty(loggerStr))
                        {
                            log.Logger = loggerStr.Trim();
                        }
                    }
                    var levelReg = regMatch.Groups["level"];
                    if (levelReg.Success)
                    {
                        var levelStr = levelReg.Value;
                        if (!string.IsNullOrEmpty(levelStr))
                        {
                            if(short.TryParse(levelStr.Trim(),out short level))
                            {
                                log.Level = (LogLevel)level;
                            }
                        }
                    }
                    var threadidReg = regMatch.Groups["threadid"];
                    if (threadidReg.Success)
                    {
                        var threadidStr = threadidReg.Value;
                        if (!string.IsNullOrEmpty(threadidStr))
                        {
                            if (int.TryParse(threadidStr.Trim(), out int threadId))
                            {
                                log.ThreadId = threadId;
                            }
                        }
                    }
                    var messageReg = regMatch.Groups["message"];
                    if (messageReg.Success)
                    {
                        log.Message = messageReg.Value;
                    }
                }
                return log;
            }
        }
        static void Main(string[] args)
        {
            var logZipFileInfo = new FileInfo("Run_2018-10-31.zip");
            // 直接在压缩文件里读取内容并解析
            Stopwatch watch = new Stopwatch();
            watch.Start();
            var logs = ReadLogsFromZip(logZipFileInfo.FullName).GetAwaiter().GetResult();
            watch.Stop();
            Console.WriteLine($"Running {watch.ElapsedMilliseconds}ms");
            
            Console.WriteLine($"从压缩包中 读取到日志{logs.Count}条");
            Console.WriteLine("---------------");

            var logFileInfo = new FileInfo("Run_2018-10-31.log");
            // 从日志文件里读取内容并解析
            watch.Reset();
            watch.Start();
            logs = ReadLogsFromLog(logFileInfo.FullName).GetAwaiter().GetResult();
            watch.Stop();
            Console.WriteLine($"Running {watch.ElapsedMilliseconds}ms");
            Console.WriteLine($"从文件中 读取到日志{logs.Count}条");
            Console.WriteLine("---------------");
            // 输出到控制台
            //foreach (var log in logs)
            //{
            //    Console.WriteLine(log);
            //}
        }
        static readonly Regex lineRegex = new Regex(@"^(?<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{4})\|(?<logger>(\s|[a-zA-Z0-9$_]){20})\|(?<level>(\s|[A-Z]){5})\|(?<threadid>(\s|\d){5})\|(?<message>.*?)$");
        static async Task<IReadOnlyList<LogRecord>> ReadLogsFromZip(string logZipPath)
        {
            var logRecords = new List<LogRecord>(3000);
            await Task.Run(()=> {
                using (ZipArchive logZipFile = ZipFile.Open(logZipPath, ZipArchiveMode.Read))
                {
                    foreach (var logFile in logZipFile.Entries)
                    {
                        using (var logStream = logFile.Open())
                        {
                            var reader = new StreamReader(logStream);
                            LogRecord log = null;
                            string exception = string.Empty;
                            while (!reader.EndOfStream)
                            {
                                var line = reader.ReadLine();
                                var regMatch = lineRegex.Match(line);
                                if (regMatch.Success) //每次匹配到新的log，则记录上一次日志项。
                                {
                                    if (log != null)
                                    {
                                        log.Exception = exception;
                                        logRecords.Add(log);
                                    }
                                    log = LogRecord.ReadFromRegexMatch(regMatch);
                                    exception = string.Empty;
                                }
                                else
                                {
                                    exception += line;
                                }
                            }
                            if (log != null)
                            {
                                log.Exception = exception;
                                logRecords.Add(log);
                            }
                        }
                    }
                }
            });
            return logRecords;
        }
        // 从日志文件中读取记录
        static async Task<IReadOnlyList<LogRecord>> ReadLogsFromLog(string logPath)
        {
            var logRecords = new List<LogRecord>(3000);
            await Task.Run(() => {
                using (var logStream = File.Open(logPath, FileMode.Open))
                {
                    var reader = new StreamReader(logStream);
                    LogRecord log = null;
                    string exception = string.Empty;
                    while (!reader.EndOfStream)
                    {
                        var line = reader.ReadLine();
                        var regMatch = lineRegex.Match(line);
                        if (regMatch.Success) //每次匹配到新的log，则记录上一次日志项。
                        {
                            if (log != null)
                            {
                                log.Exception = exception;
                                logRecords.Add(log);
                            }
                            log = LogRecord.ReadFromRegexMatch(regMatch);
                            exception = string.Empty;
                        }
                        else
                        {
                            exception += line;
                        }
                    }
                    if (log != null)
                    {
                        log.Exception = exception;
                        logRecords.Add(log);
                    }
                }
            });
            return logRecords;
        }


    }
}
